package com.luooqi.ocr.utils;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.hutool.log.StaticLog;
import com.luooqi.ocr.model.TextBlock;

import java.awt.*;
import java.util.*;
import java.util.List;

/**
 * tools-ocr
 * Created by 何志龙 on 2019-03-22.
 */
public class OcrUtils {

    public static String ocrImg(byte[] imgData) {
        int i = Math.abs(UUID.randomUUID().hashCode()) % 4;
        StaticLog.info("OCR Engine: " + i);
        switch (i){
            case 0:
                return bdGeneralOcr(imgData);
            case 1:
                return bdAccurateOcr(imgData);
            case 2:
                return sogouMobileOcr(imgData);
            default:
                return sogouWebOcr(imgData);
        }
    }

    private static String bdGeneralOcr(byte[] imgData){
        return bdBaseOcr(imgData, "general_location");
    }

    private static String bdAccurateOcr(byte[] imgData){
        return bdBaseOcr(imgData, "https://aip.baidubce.com/rest/2.0/ocr/v1/accurate");
    }

    private static String bdBaseOcr(byte[] imgData, String type){
        String[] urlArr = new String[]{"http://ai.baidu.com/tech/ocr/general", "http://ai.baidu.com/index/seccode?action=show"};
        StringBuilder cookie = new StringBuilder();
        for (String url : urlArr) {
            HttpResponse cookieResp = WebUtils.get(url);
            List<String> ckList = cookieResp.headerList("Set-Cookie");
            for (String s : ckList) {
                cookie.append(s.replaceAll("expires[\\S\\s]+", ""));
            }
        }
        HashMap<String, String> header = new HashMap<>();
        header.put("Referer", "http://ai.baidu.com/tech/ocr/general");
        header.put("Cookie", cookie.toString());
        String data = "type="+URLUtil.encodeQuery(type)+"&detect_direction=false&image_url&image=" + URLUtil.encodeQuery("data:image/jpeg;base64," + Base64.encode(imgData)) + "&language_type=CHN_ENG";
        HttpResponse response = WebUtils.postRaw("http://ai.baidu.com/aidemo", data, 0, header);
        return extractBdResult(WebUtils.getSafeHtml(response));
    }

    public static String sogouMobileOcr(byte[] imgData) {
        String boundary = "------WebKitFormBoundary8orYTmcj8BHvQpVU";
        String url = "http://ocr.shouji.sogou.com/v2/ocr/json";
        String header = boundary + "\r\nContent-Disposition: form-data; name=\"pic\"; filename=\"pic.jpg\"\r\nContent-Type: image/jpeg\r\n\r\n";
        String footer = "\r\n" + boundary + "--\r\n";
        byte[] postData = CommUtils.mergeByte(header.getBytes(CharsetUtil.CHARSET_ISO_8859_1), imgData, footer.getBytes(CharsetUtil.CHARSET_ISO_8859_1));
        return extractSogouResult(CommUtils.postMultiData(url, postData, boundary.substring(2)));
    }

    public static String sogouWebOcr(byte[] imgData) {
        String url = "https://deepi.sogou.com/api/sogouService";
        String referer = "https://deepi.sogou.com/?from=picsearch&tdsourcetag=s_pctim_aiomsg";
        String imageData = Base64.encode(imgData);
        long t = System.currentTimeMillis();
        String sign = SecureUtil.md5("sogou_ocr_just_for_deepibasicOpenOcr" + t + imageData.substring(0, Math.min(1024, imageData.length())) + "4b66a37108dab018ace616c4ae07e644");
        Map<String, Object> data = new HashMap<>();
        data.put("image", imageData);
        data.put("lang", "zh-Chs");
        data.put("pid", "sogou_ocr_just_for_deepi");
        data.put("salt", t);
        data.put("service", "basicOpenOcr");
        data.put("sign", sign);
        HttpRequest request = HttpUtil.createPost(url).timeout(15000);
        request.form(data);
        request.header("Referer", referer);
        HttpResponse response = request.execute();
        return extractSogouResult(WebUtils.getSafeHtml(response));
    }

    private static String extractSogouResult(String html) {
        if (StrUtil.isBlank(html)) {
            return "";
        }
        JSONObject jsonObject = JSONUtil.parseObj(html);
        if (jsonObject.getInt("success", 0) != 1) {
            return "";
        }
        JSONArray jsonArray = jsonObject.getJSONArray("result");
        List<TextBlock> textBlocks = new ArrayList<>();
        boolean isEng;
        for (int i = 0; i < jsonArray.size(); i++) {
            JSONObject jObj = jsonArray.getJSONObject(i);
            TextBlock textBlock = new TextBlock();
            textBlock.setText(jObj.getStr("content").trim());
            //noinspection SuspiciousToArrayCall
            String[] frames = jObj.getJSONArray("frame").toArray(new String[0]);
            textBlock.setTopLeft(CommUtils.frameToPoint(frames[0]));
            textBlock.setTopRight(CommUtils.frameToPoint(frames[1]));
            textBlock.setBottomRight(CommUtils.frameToPoint(frames[2]));
            textBlock.setBottomLeft(CommUtils.frameToPoint(frames[3]));
            textBlocks.add(textBlock);
        }
        isEng = jsonObject.getStr("lang", "zh-Chs").equals("zh-Chs");
        return CommUtils.combineTextBlocks(textBlocks, isEng);
    }

    private static String extractBdResult(String html) {
        if (StrUtil.isBlank(html)) {
            return "";
        }
        JSONObject jsonObject = JSONUtil.parseObj(html);
        if (jsonObject.getInt("errno", 0) != 0) {
            return "";
        }
        JSONArray jsonArray = jsonObject.getJSONObject("data").getJSONArray("words_result");
        List<TextBlock> textBlocks = new ArrayList<>();
        boolean isEng = false;
        for (int i = 0; i < jsonArray.size(); i++) {
            JSONObject jObj = jsonArray.getJSONObject(i);
            TextBlock textBlock = new TextBlock();
            textBlock.setText(jObj.getStr("words").trim());
            //noinspection SuspiciousToArrayCall
            JSONObject location = jObj.getJSONObject("location");
            int top = location.getInt("top");
            int left = location.getInt("left");
            int width = location.getInt("width");
            int height = location.getInt("height");
            textBlock.setTopLeft(new Point(top, left));
            textBlock.setTopRight(new Point(top, left + width));
            textBlock.setBottomLeft(new Point(top + height, left));
            textBlock.setBottomRight(new Point(top + height, left + width));
            textBlocks.add(textBlock);
        }
        return CommUtils.combineTextBlocks(textBlocks, isEng);
    }

}
